"""数据同步相关的视图

定义数据同步相关的API视图，包括同步会话管理、冲突解决等。
"""

from rest_framework import status, permissions
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework.views import APIView
from django.db.models import Q, F
from django.utils import timezone
from django.utils.dateparse import parse_datetime
from django.shortcuts import get_object_or_404
from django.db import transaction
from datetime import datetime, timedelta

from .sync_models import SyncSession, SyncItem, ConflictResolution
from .sync_serializers import (
    SyncSessionSerializer,
    SyncItemSerializer,
    ConflictResolutionSerializer,
    SyncRequestSerializer,
    SyncResponseSerializer,
    ItemSyncSerializer,
    ConflictItemSerializer
)
from .models import Note, Category, Tag
from .serializers import NoteDetailSerializer as NoteSerializer, CategorySerializer, TagSerializer
from apps.common.models import ActivityLog


class SyncSessionViewSet(ModelViewSet):
    """同步会话视图集
    
    处理同步会话的创建和管理。
    """
    
    serializer_class = SyncSessionSerializer
    permission_classes = [permissions.IsAuthenticated]
    
    def get_queryset(self):
        """获取查询集"""
        return SyncSession.objects.filter(
            user=self.request.user
        ).order_by('-started_at')
    
    @action(detail=True, methods=['post'])
    def complete(self, request, pk=None):
        """完成同步会话"""
        session = self.get_object()
        
        if session.status != 'in_progress':
            return Response(
                {'error': '同步会话状态错误'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        session.mark_completed()
        
        serializer = self.get_serializer(session)
        return Response(serializer.data)
    
    @action(detail=True, methods=['post'])
    def fail(self, request, pk=None):
        """标记同步会话失败"""
        session = self.get_object()
        error_message = request.data.get('error_message', '同步失败')
        
        session.mark_failed(error_message)
        
        serializer = self.get_serializer(session)
        return Response(serializer.data)


class SyncItemViewSet(ModelViewSet):
    """同步项目视图集
    
    处理同步项目的管理。
    """
    
    serializer_class = SyncItemSerializer
    permission_classes = [permissions.IsAuthenticated]
    
    def get_queryset(self):
        """获取查询集"""
        return SyncItem.objects.filter(
            session__user=self.request.user
        ).select_related('session').order_by('-created_at')
    
    @action(detail=True, methods=['post'])
    def resolve_conflict(self, request, pk=None):
        """解决冲突"""
        sync_item = self.get_object()
        
        if sync_item.status != 'conflict':
            return Response(
                {'error': '该项目没有冲突'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        serializer = ConflictResolutionSerializer(
            data=request.data,
            context={'request': request}
        )
        serializer.is_valid(raise_exception=True)
        
        # 创建冲突解决方案
        resolution = serializer.save(sync_item=sync_item)
        
        # 标记同步项目为已解决
        sync_item.mark_synced()
        
        return Response({
            'message': '冲突已解决',
            'resolution': ConflictResolutionSerializer(resolution).data
        })


class DataSyncAPIView(APIView):
    """数据同步API视图
    
    处理客户端的数据同步请求。
    """
    
    permission_classes = [permissions.IsAuthenticated]
    
    def post(self, request):
        """处理同步请求
        
        使用手动事务管理来避免事务管理错误。
        """
        serializer = SyncRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        data = serializer.validated_data
        user = request.user
        
        # 创建同步会话（在事务外）
        session = SyncSession.objects.create(
            user=user,
            status='in_progress',
            client_version=data.get('client_version', ''),
            device_id=data.get('device_id', '')
        )
        
        try:
            # 使用事务处理同步数据
            with transaction.atomic():
                sync_result = self._process_sync_data(
                    session,
                    data,
                    user
                )
            
            # 在事务外标记会话完成
            session.mark_completed()
            
            # 返回同步结果
            response_data = {
                'session_id': session.id,
                'status': 'completed',
                'sync_time': timezone.now(),
                'items_uploaded': sync_result['items_uploaded'],
                'items_downloaded': sync_result['items_downloaded'],
                'conflicts_detected': sync_result['conflicts_detected'],
                'notes': sync_result['notes'],
                'categories': sync_result['categories'],
                'tags': sync_result['tags'],
                'conflicts': sync_result['conflicts']
            }
            
            response_serializer = SyncResponseSerializer(data=response_data)
            response_serializer.is_valid(raise_exception=True)
            
            return Response(response_serializer.data)
            
        except Exception as e:
            # 在事务外标记会话失败
            try:
                session.mark_failed(str(e))
            except Exception:
                # 如果标记失败也失败了，记录日志
                import logging
                logger = logging.getLogger(__name__)
                logger.error(f'同步会话标记失败: {str(e)}')
            
            # 返回错误响应而不是抛出异常
            return Response(
                {'error': f'同步失败: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    def _process_sync_data(self, session, data, user):
        """处理同步数据"""
        result = {
            'items_uploaded': 0,
            'items_downloaded': 0,
            'conflicts_detected': 0,
            'notes': [],
            'categories': [],
            'tags': [],
            'conflicts': []
        }
        
        last_sync_time = data.get('last_sync_time')
        
        # 处理分类
        if 'categories' in data:
            category_result = self._sync_categories(
                session, data['categories'], user, last_sync_time
            )
            result.update(category_result)
        
        # 处理标签
        if 'tags' in data:
            tag_result = self._sync_tags(
                session, data['tags'], user, last_sync_time
            )
            result.update(tag_result)
        
        # 处理笔记
        if 'notes' in data:
            note_result = self._sync_notes(
                session, data['notes'], user, last_sync_time
            )
            result.update(note_result)
        
        # 获取服务器端更新的数据
        server_updates = self._get_server_updates(user, last_sync_time)
        result['notes'].extend(server_updates['notes'])
        result['categories'].extend(server_updates['categories'])
        result['tags'].extend(server_updates['tags'])
        result['items_downloaded'] += server_updates['count']
        
        # 更新会话统计
        session.items_uploaded = result['items_uploaded']
        session.items_downloaded = result['items_downloaded']
        session.conflicts_detected = result['conflicts_detected']
        session.save()
        
        return result
    
    def _sync_categories(self, session, categories, user, last_sync_time):
        """同步分类数据"""
        result = {
            'items_uploaded': 0,
            'conflicts_detected': 0,
            'categories': []
        }
        
        for category_data in categories:
            try:
                # 使用get_or_create避免唯一性约束冲突
                sync_item, created = SyncItem.objects.get_or_create(
                    session=session,
                    item_type='category',
                    item_id=str(category_data.get('id', '')),
                    defaults={
                        'action': category_data.get('action', 'create'),
                        'client_data': category_data,
                        'client_timestamp': category_data.get('updated_at')
                    }
                )
                
                # 如果不是新创建的，更新数据
                if not created:
                    sync_item.action = category_data.get('action', 'create')
                    sync_item.client_data = category_data
                    sync_item.client_timestamp = category_data.get('updated_at')
                    sync_item.status = 'pending'
                    sync_item.error_message = ''
                    sync_item.save()
                
                # 处理分类同步逻辑
                self._process_category_sync(sync_item, user)
                result['items_uploaded'] += 1
                
            except Exception as e:
                # 创建一个临时的sync_item用于错误记录
                try:
                    error_item, _ = SyncItem.objects.get_or_create(
                        session=session,
                        item_type='category',
                        item_id=str(category_data.get('id', 'unknown')),
                        defaults={
                            'action': 'error',
                            'client_data': category_data,
                            'status': 'error',
                            'error_message': str(e)
                        }
                    )
                    if not _:
                        error_item.mark_error(str(e))
                except Exception:
                    # 如果连错误记录都失败，至少记录到日志
                    import logging
                    logger = logging.getLogger(__name__)
                    logger.error(f'同步分类失败: {str(e)}')
                    pass
        
        return result
    
    def _sync_tags(self, session, tags, user, last_sync_time):
        """同步标签数据"""
        result = {
            'items_uploaded': 0,
            'conflicts_detected': 0,
            'tags': []
        }
        
        for tag_data in tags:
            try:
                # 使用get_or_create避免唯一性约束冲突
                sync_item, created = SyncItem.objects.get_or_create(
                    session=session,
                    item_type='tag',
                    item_id=str(tag_data.get('id', '')),
                    defaults={
                        'action': tag_data.get('action', 'create'),
                        'client_data': tag_data,
                        'client_timestamp': tag_data.get('updated_at')
                    }
                )
                
                # 如果不是新创建的，更新数据
                if not created:
                    sync_item.action = tag_data.get('action', 'create')
                    sync_item.client_data = tag_data
                    sync_item.client_timestamp = tag_data.get('updated_at')
                    sync_item.status = 'pending'
                    sync_item.error_message = ''
                    sync_item.save()
                
                # 处理标签同步逻辑
                self._process_tag_sync(sync_item, user)
                result['items_uploaded'] += 1
                
            except Exception as e:
                # 创建一个临时的sync_item用于错误记录
                try:
                    error_item, _ = SyncItem.objects.get_or_create(
                        session=session,
                        item_type='tag',
                        item_id=str(tag_data.get('id', 'unknown')),
                        defaults={
                            'action': 'error',
                            'client_data': tag_data,
                            'status': 'error',
                            'error_message': str(e)
                        }
                    )
                    if not _:
                        error_item.mark_error(str(e))
                except Exception:
                    # 如果连错误记录都失败，至少记录到日志
                    import logging
                    logger = logging.getLogger(__name__)
                    logger.error(f'同步标签失败: {str(e)}')
                    pass
        
        return result
    
    def _sync_notes(self, session, notes, user, last_sync_time):
        """同步笔记数据"""
        result = {
            'items_uploaded': 0,
            'conflicts_detected': 0,
            'notes': []
        }
        
        for note_data in notes:
            try:
                # 使用get_or_create避免唯一性约束冲突
                sync_item, created = SyncItem.objects.get_or_create(
                    session=session,
                    item_type='note',
                    item_id=str(note_data.get('id', '')),
                    defaults={
                        'action': note_data.get('action', 'create'),
                        'client_data': note_data,
                        'client_timestamp': note_data.get('updated_at')
                    }
                )
                
                # 如果不是新创建的，更新数据
                if not created:
                    sync_item.action = note_data.get('action', 'create')
                    sync_item.client_data = note_data
                    sync_item.client_timestamp = note_data.get('updated_at')
                    sync_item.status = 'pending'
                    sync_item.error_message = ''
                    sync_item.save()
                
                # 处理笔记同步逻辑
                self._process_note_sync(sync_item, user)
                result['items_uploaded'] += 1
                
            except Exception as e:
                # 创建一个临时的sync_item用于错误记录
                try:
                    error_item, _ = SyncItem.objects.get_or_create(
                        session=session,
                        item_type='note',
                        item_id=str(note_data.get('id', 'unknown')),
                        defaults={
                            'action': 'error',
                            'client_data': note_data,
                            'status': 'error',
                            'error_message': str(e)
                        }
                    )
                    if not _:
                        error_item.mark_error(str(e))
                except Exception:
                    # 如果连错误记录都失败，至少记录到日志
                    import logging
                    logger = logging.getLogger(__name__)
                    logger.error(f'同步笔记失败: {str(e)}')
                    pass
        
        return result
    
    def _process_category_sync(self, sync_item, user):
        """处理分类同步
        
        处理前端发送的分类数据，进行字段映射和转换。
        """
        action = sync_item.action
        data = sync_item.client_data
        
        if action == 'create':
            # 处理前端数据格式
            from .sync_serializers import FrontendCategorySerializer
            frontend_serializer = FrontendCategorySerializer(data=data)
            
            if frontend_serializer.is_valid():
                # 转换为后端格式
                internal_data = frontend_serializer.validated_data
                
                # 创建新分类
                category_data = {
                    'name': internal_data['name'],
                    'description': internal_data.get('description', ''),
                    'color': internal_data.get('color', '#007bff'),
                }
                
                # 处理父分类
                if internal_data.get('parent_id'):
                    try:
                        # 这里需要映射前端ID到后端UUID
                        parent_category = self._find_category_by_frontend_id(
                            internal_data['parent_id'], user
                        )
                        if parent_category:
                            category_data['parentId'] = parent_category
                    except Exception:
                        pass  # 忽略父分类映射错误
                
                serializer = CategorySerializer(data=category_data)
                if serializer.is_valid():
                    category = serializer.save(created_by=user)
                    
                    # 保存前端ID映射关系
                    if internal_data.get('frontend_id'):
                        self._save_id_mapping('category', internal_data['frontend_id'], category.id)
                    
                    sync_item.mark_synced()
                else:
                    sync_item.mark_error(str(serializer.errors))
            else:
                sync_item.mark_error(str(frontend_serializer.errors))
        
        elif action == 'update':
            # 更新分类
            try:
                category = Category.objects.get(
                    id=data['id'],
                    created_by=user
                )
                
                # 检查冲突
                if self._has_conflict(sync_item, category.updated_at):
                    sync_item.mark_conflict('数据冲突')
                    return
                
                serializer = CategorySerializer(
                    category,
                    data=data,
                    partial=True
                )
                if serializer.is_valid():
                    serializer.save()
                    sync_item.mark_synced()
                else:
                    sync_item.mark_error(str(serializer.errors))
                    
            except Category.DoesNotExist:
                sync_item.mark_error('分类不存在')
        
        elif action == 'delete':
            # 删除分类
            try:
                category = Category.objects.get(
                    id=data['id'],
                    created_by=user
                )
                category.delete()
                sync_item.mark_synced()
            except Category.DoesNotExist:
                sync_item.mark_synced()  # 已经不存在，视为成功
    
    def _process_tag_sync(self, sync_item, user):
        """处理标签同步"""
        # 类似于分类同步的逻辑
        pass
    
    def _process_note_sync(self, sync_item, user):
        """处理笔记同步
        
        处理前端发送的笔记数据，进行字段映射和转换。
        """
        action = sync_item.action
        data = sync_item.client_data
        
        if action == 'create':
            # 处理前端数据格式
            from .sync_serializers import FrontendNoteSerializer
            frontend_serializer = FrontendNoteSerializer(data=data)
            
            if frontend_serializer.is_valid():
                # 转换为后端格式
                internal_data = frontend_serializer.validated_data
                
                # 创建新笔记
                note_data = {
                    'title': internal_data['title'],
                    'content': internal_data['content'],
                    'status': internal_data.get('status', 'draft'),
                }
                
                # 处理分类映射（folderId -> folderId）
                if internal_data.get('category_id'):
                    try:
                        # 映射前端分类ID到后端分类
                        backend_category_id = self._get_backend_id('category', internal_data['category_id'])
                        if backend_category_id:
                            category = Category.objects.get(id=backend_category_id, created_by=user)
                            note_data['folderId'] = category
                        else:
                            # 如果找不到映射，尝试查找默认分类
                            default_category = Category.objects.filter(
                                created_by=user, 
                                name__in=['全部笔记', '未分类笔记']
                            ).first()
                            if default_category:
                                note_data['folderId'] = default_category
                    except Exception:
                        pass  # 忽略分类映射错误
                
                # 使用笔记序列化器创建
                from .serializers import NoteCreateUpdateSerializer
                serializer = NoteCreateUpdateSerializer(data=note_data)
                if serializer.is_valid():
                    note = serializer.save(author=user)
                    
                    # 保存前端ID映射关系
                    if internal_data.get('frontend_id'):
                        self._save_id_mapping('note', internal_data['frontend_id'], note.id)
                    
                    sync_item.mark_synced()
                else:
                    sync_item.mark_error(str(serializer.errors))
            else:
                sync_item.mark_error(str(frontend_serializer.errors))
        
        elif action == 'update':
            # 更新笔记逻辑
            try:
                # 根据前端ID查找对应的后端笔记
                frontend_id = data.get('id')
                if frontend_id:
                    backend_id = self._get_backend_id('note', frontend_id)
                    if backend_id:
                        note = Note.objects.get(id=backend_id, author=user)
                        
                        # 处理前端数据
                        from .sync_serializers import FrontendNoteSerializer
                        frontend_serializer = FrontendNoteSerializer(data=data)
                        
                        if frontend_serializer.is_valid():
                            internal_data = frontend_serializer.validated_data
                            
                            # 检查冲突
                            if self._has_conflict(sync_item, note.updated_at):
                                sync_item.mark_conflict('数据冲突')
                                return
                            
                            # 更新笔记数据
                            note_data = {
                                'title': internal_data['title'],
                                'content': internal_data['content'],
                            }
                            
                            from .serializers import NoteCreateUpdateSerializer
                            serializer = NoteCreateUpdateSerializer(
                                note, data=note_data, partial=True
                            )
                            if serializer.is_valid():
                                serializer.save()
                                sync_item.mark_synced()
                            else:
                                sync_item.mark_error(str(serializer.errors))
                        else:
                            sync_item.mark_error(str(frontend_serializer.errors))
                    else:
                        sync_item.mark_error('找不到对应的笔记')
                else:
                    sync_item.mark_error('缺少笔记ID')
            except Note.DoesNotExist:
                sync_item.mark_error('笔记不存在')
            except Exception as e:
                sync_item.mark_error(f'更新笔记时出错: {str(e)}')
        
        elif action == 'delete':
            # 删除笔记逻辑
            try:
                frontend_id = data.get('id')
                if frontend_id:
                    backend_id = self._get_backend_id('note', frontend_id)
                    if backend_id:
                        note = Note.objects.get(id=backend_id, author=user)
                        note.delete()
                        sync_item.mark_synced()
                    else:
                        sync_item.mark_synced()  # 已经不存在，视为成功
                else:
                    sync_item.mark_error('缺少笔记ID')
            except Note.DoesNotExist:
                sync_item.mark_synced()  # 已经不存在，视为成功
            except Exception as e:
                sync_item.mark_error(f'删除笔记时出错: {str(e)}')
    
    def _has_conflict(self, sync_item, server_timestamp):
        """检查是否存在冲突"""
        client_timestamp = sync_item.client_timestamp
        if not client_timestamp or not server_timestamp:
            return False
        
        # 确保时间戳都是datetime对象
        if isinstance(client_timestamp, str):
            try:
                if 'T' in client_timestamp:
                    client_timestamp = parse_datetime(client_timestamp)
                else:
                    client_timestamp = datetime.strptime(client_timestamp, '%Y年%m月%d日 %H:%M')
            except (ValueError, TypeError):
                return False
        
        if isinstance(server_timestamp, str):
            try:
                if 'T' in server_timestamp:
                    server_timestamp = parse_datetime(server_timestamp)
                else:
                    server_timestamp = datetime.strptime(server_timestamp, '%Y年%m月%d日 %H:%M')
            except (ValueError, TypeError):
                return False
        
        # 如果服务器时间戳更新，则存在冲突
        return server_timestamp > client_timestamp
    
    def _find_category_by_frontend_id(self, frontend_id, user):
        """根据前端ID查找分类
        
        Args:
            frontend_id: 前端分类ID
            user: 用户对象
            
        Returns:
            Category对象或None
        """
        # 这里可以通过缓存或数据库映射表来查找
        # 暂时使用简单的逻辑：如果是默认分类ID，返回None
        if frontend_id in [1, 2]:  # 前端默认分类ID
            return None
        
        # 可以扩展为更复杂的映射逻辑
        try:
            # 尝试通过名称匹配（临时方案）
            return Category.objects.filter(created_by=user).first()
        except:
            return None
    
    def _save_id_mapping(self, item_type, frontend_id, backend_id):
        """保存前端ID到后端ID的映射关系
        
        Args:
            item_type: 项目类型 ('category', 'note', 'tag')
            frontend_id: 前端ID
            backend_id: 后端UUID
        """
        # 这里可以保存到缓存或专门的映射表中
        # 暂时使用简单的内存缓存
        if not hasattr(self, '_id_mappings'):
            self._id_mappings = {}
        
        if item_type not in self._id_mappings:
            self._id_mappings[item_type] = {}
            
        self._id_mappings[item_type][frontend_id] = backend_id
    
    def _get_backend_id(self, item_type, frontend_id):
        """获取前端ID对应的后端ID
        
        Args:
            item_type: 项目类型
            frontend_id: 前端ID
            
        Returns:
            后端UUID或None
        """
        if not hasattr(self, '_id_mappings'):
            return None
            
        return self._id_mappings.get(item_type, {}).get(frontend_id)
    
    def _get_server_updates(self, user, last_sync_time):
        """获取服务器端的更新数据"""
        result = {
            'notes': [],
            'categories': [],
            'tags': [],
            'count': 0
        }
        
        if not last_sync_time:
            # 首次同步，返回所有数据
            notes = Note.objects.filter(author=user)
            categories = Category.objects.filter(created_by=user)
            tags = Tag.objects.filter(created_by=user)
        else:
            # 增量同步，只返回更新的数据
            notes = Note.objects.filter(
                author=user,
                updated_at__gt=last_sync_time
            )
            categories = Category.objects.filter(
                created_by=user,
                updated_at__gt=last_sync_time
            )
            tags = Tag.objects.filter(
                created_by=user,
                updated_at__gt=last_sync_time
            )
        
        # 序列化数据
        result['notes'] = NoteSerializer(notes, many=True).data
        result['categories'] = CategorySerializer(categories, many=True).data
        result['tags'] = TagSerializer(tags, many=True).data
        result['count'] = notes.count() + categories.count() + tags.count()
        
        return result


class ConflictResolutionViewSet(ModelViewSet):
    """冲突解决视图集
    
    处理数据冲突的解决。
    """
    
    serializer_class = ConflictResolutionSerializer
    permission_classes = [permissions.IsAuthenticated]
    
    def get_queryset(self):
        """获取查询集"""
        return ConflictResolution.objects.filter(
            sync_item__session__user=self.request.user
        ).select_related('sync_item', 'resolved_by').order_by('-resolved_at')
    
    @action(detail=False, methods=['get'])
    def pending_conflicts(self, request):
        """获取待解决的冲突"""
        conflicts = SyncItem.objects.filter(
            session__user=request.user,
            status='conflict'
        ).select_related('session')
        
        conflict_data = []
        for conflict in conflicts:
            conflict_data.append({
                'item_type': conflict.item_type,
                'item_id': conflict.item_id,
                'client_data': conflict.client_data,
                'server_data': conflict.server_data,
                'client_timestamp': conflict.client_timestamp,
                'server_timestamp': conflict.server_timestamp,
                'conflict_reason': '数据版本冲突'
            })
        
        serializer = ConflictItemSerializer(conflict_data, many=True)
        return Response(serializer.data)